﻿using System;
using System.Linq;
using System.Windows.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Xml.Linq;

using Perseus;
using Perseus.Plugins;

using Hetymine.Executioner.Commands;
using Hetymine.Executioner.Data;
using Hetymine.Executioner.Plugins;
using Hetymine.Executioner.Triggers;

namespace Hetymine.Executioner.Controls {
    /// <summary>
    /// Interaction logic for TriggerCommandConfigurator.xaml
    /// </summary>
    public partial class TriggerCommandConfigurator : UserControl, ICommandConfigurator<ICommand>, IScrollable, ILoader {
        private TriggerCommand _Command;
        private bool _IsApply = false;
        private DispatcherTimer _Timer;
        private bool _IsEmptyTriggers = true;

        public TriggerCommandConfigurator() {
            InitializeComponent();

            this.cName.TextChanged += new TextChangedEventHandler(Name_TextChanged);
            this.cCallStrings.TextChanged += new TextChangedEventHandler(CallStrings_TextChanged);
            this.cKeepHistory.Click += new RoutedEventHandler(KeepHistory_Click);

            this.cAddCommand.Click += new RoutedEventHandler(AddCommand_Click);
            this.cRemoveCommand.Click += new RoutedEventHandler(RemoveCommand_Click);

            this.cAddTrigger.Click += new RoutedEventHandler(AddTrigger_Click);
            this.cRemoveTrigger.Click += new RoutedEventHandler(RemoveTrigger_Click);
            this.InitializeTriggerMenu();
            this.cTriggers.ScrollChanged += new ScrollChangedEventHandler(Triggers_ScrollChanged);
            
            this.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
            this.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;

            if (this.cAddCommand.ContextMenu.HasItems) {
                this.cButtons.Visibility = System.Windows.Visibility.Visible;
                this.cTriggers.Visibility = System.Windows.Visibility.Visible;
                this.cNoTriggersInstalled.Visibility = System.Windows.Visibility.Collapsed;
            }
            else {
                this.cButtons.Visibility = System.Windows.Visibility.Collapsed;
                this.cTriggers.Visibility = System.Windows.Visibility.Collapsed;
                this.cNoTriggersInstalled.Visibility = System.Windows.Visibility.Visible;
            }
            this.cNoTriggersAdded.Visibility = System.Windows.Visibility.Collapsed;

            // Hacky work around for triggers not having update event just check every 250 ms
            this._Timer = new DispatcherTimer();
            this._Timer.Interval = TimeSpan.FromMilliseconds(200);
            this._Timer.IsEnabled = false;
            this._Timer.Tick += new EventHandler(_Timer_Tick);
        }
        
        private void InitializeTriggerMenu() {
            ContextMenu cm = new ContextMenu();
            MenuItem mi;
           
            var plugins = from p in Executioner.PluginService.Plugins
                          where p.Instance is TriggerPlugin
                          select p;

            foreach (PluginInstance<IPlugin> p in plugins) {
                mi = new MenuItem();
                mi.Header = p.Instance.PluginInfo.Name;
                mi.Tag = p.FullName;
                mi.Click += new RoutedEventHandler(TriggerMenu_Click);
                cm.Items.Add(mi);
            }

            this.cAddCommand.ContextMenu = cm;
        }
        
        #region Control Events
        private void Name_TextChanged(object sender, TextChangedEventArgs e) {
            if (!this._IsApply) { return; }

            this._Command.Name = this.cName.Text.Trim();

            this.OnCommandNameChanged(EventArgs.Empty);
        }
        private void CallStrings_TextChanged(object sender, TextChangedEventArgs e) {
            if (!this._IsApply) { return; }

            string[] calls = this.cCallStrings.Text.Split(
                ",",
                SplitOptions.Trim | SplitOptions.RemoveEmptyEntries
            );

            this._Command.CallStrings.Clear();
            this._Command.CallStrings.AddRange(calls);

            this.OnCommandNameChanged(EventArgs.Empty);
        }
        private void KeepHistory_Click(object sender, RoutedEventArgs e) {
            if (this.cKeepHistory.IsChecked == null) {
                this.cHistoryThirdState.Visibility = Visibility.Visible;
            }
            else {
                this.cHistoryThirdState.Visibility = Visibility.Collapsed;
            }

            this._Command.KeepHistory = this.cKeepHistory.IsChecked;
        }      

        private void CommandSelector_CommandChanged(object sender, EventArgs e) {
            if (!this._IsApply) { return; }

            CommandSelector cs = sender as CommandSelector;

            CommandInfo ci = new CommandInfo(
                cs.Command,
                cs.Arguments
            );
            
            int tabIndex = this.cCommandTabs.SelectedIndex;
            this._Command.Commands[tabIndex] = ci;            

            this.OnCommandNameChanged(EventArgs.Empty);
        }
        private void AddCommand_Click(object sender, RoutedEventArgs e) {
            if (this.cCommandTabs.Items.Count == 8) { return; }

            this._Command.Commands.Add(new CommandInfo());

            TabItem ti = this.CreateCommandTabItem(this.cCommandTabs.Items.Count);
            this.cCommandTabs.Items.Add(ti);

            if (this.cCommandTabs.Items.Count == 8) {
                this.cAddCommand.IsEnabled = false;
            }

            this.cCommandTabs.SelectedIndex = this.cCommandTabs.Items.Count - 1;
        }
        private void RemoveCommand_Click(object sender, RoutedEventArgs e) {
            this.cAddCommand.IsEnabled = true;

            if (this.cCommandTabs.Items.Count == 1) {
                this._Command.Commands.Clear();
                this.InitializeCommands();
            }
            else {
                int tabIndex = this.cCommandTabs.SelectedIndex;
                this._Command.Commands.RemoveAt(tabIndex);
                this.cCommandTabs.Items.RemoveAt(tabIndex);

                // If the last item was removed
                if (this.cCommandTabs.Items.Count <= tabIndex) {
                    this.cCommandTabs.SelectedIndex = tabIndex - 1;
                }
                else {
                    this.cCommandTabs.SelectedIndex = tabIndex;

                    // Reorder tab headers
                    int count = 0;
                    foreach (TabItem ti in this.cCommandTabs.Items) {
                        ++count;
                        ti.Header = count.ToString();
                    }
                }
            }
        }

        private void AddTrigger_Click(object sender, RoutedEventArgs e) {
            this.cAddCommand.ContextMenu.IsOpen = true;
        }
        private void RemoveTrigger_Click(object sender, RoutedEventArgs e) {
            for (int i = this._Command.Triggers.Count - 1; i >= 0; --i) {
                if (this._Command.Triggers[i].IsSelected) {
                    this._Command.Triggers.RemoveAt(i);
                    this.cTriggerList.Children.RemoveAt(i);
                }
            }

            this.UpdateTriggerTab();
        }
        private void TriggerMenu_Click(object sender, RoutedEventArgs e) {
            var mi = sender as MenuItem;
            ITrigger trigger;

            // There are no included triggers so check plugins
            var plugin = Executioner.PluginService[mi.Tag.ToString()];
            TriggerPlugin tp = plugin.Instance as TriggerPlugin;
            trigger = tp.InitializeTrigger();            
            
            this._Command.Triggers.Add(trigger);
            this.cTriggerList.Children.Add(trigger.Configurator.ConfigPanel);

            this.UpdateTriggerTab();
        }
        private void Triggers_ScrollChanged(object sender, ScrollChangedEventArgs e) {
            if (this.cTriggers.ComputedVerticalScrollBarVisibility == Visibility.Visible) {
                this.cTriggers.Padding = new Thickness(0, 0, 5, 0);
            }
            else {
                this.cTriggers.Padding = new Thickness(0);
            }
        }
        #endregion
        #region Commands
        private void InitializeCommands() {
            this.cCommandTabs.Items.Clear();
            // If no commands add an empty one
            if (this._Command.Commands.Count == 0) {
                this._Command.Commands.Add(new CommandInfo());
            }
            foreach (CommandInfo ci in this._Command.Commands) {
                TabItem ti = this.CreateCommandTabItem(this.cCommandTabs.Items.Count, ci);
                this.cCommandTabs.Items.Add(ti);
            }
        }
        private TabItem CreateCommandTabItem(int index) {
            return this.CreateCommandTabItem(index, new CommandInfo());
        }
        private TabItem CreateCommandTabItem(int index, CommandInfo commandInfo) {
            TabItem ti = new TabItem();
            CommandSelector cs = new CommandSelector();

            ti.Header = (index + 1).ToString();
            cs.Command = commandInfo.Command;
            cs.Arguments = commandInfo.Arguments;
            cs.CommandChanged += new EventHandler(CommandSelector_CommandChanged);

            ti.Content = cs;

            return ti;
        }
        #endregion
        #region Triggers
        private void InitializeTriggers() {
            bool isEmpty = true;

            this.cTriggerList.Children.Clear();
            foreach (ITrigger t in this._Command.Triggers) {
                this.cTriggerList.Children.Add(t.Configurator.ConfigPanel);
                if (!t.IsEmpty) {
                    isEmpty = false;
                }
            }

            this._IsEmptyTriggers = isEmpty;
            this._Timer.IsEnabled = true;

            this.UpdateTriggerTab();
        }
        private void UpdateTriggerListPadding() {
            for (int i = 0; i < this.cTriggerList.Children.Count; ++i) {
                var configPanel = this.cTriggerList.Children[i] as UserControl;

                if (i == 0) {
                    configPanel.Margin = new Thickness(0);
                }
                else {
                    configPanel.Margin = new Thickness(0, 4, 0, 0);
                }
            }
        }
        private void _Timer_Tick(object sender, EventArgs e) {
            if (!this._IsApply) { return; }

            bool isEmpty = true;

            foreach (ITrigger t in this._Command.Triggers) {
                if (!t.IsEmpty) {
                    isEmpty = false;
                    break;
                }
            }

            // Update command name when triggers are present or not present
            if (this._IsEmptyTriggers != isEmpty) {
                this._IsEmptyTriggers = isEmpty;
                this.OnCommandNameChanged(EventArgs.Empty);
            }
        }
        private void UpdateTriggerTab() {
            if (this._Command.Triggers.Count == 0) {
                this.cTriggers.Visibility = System.Windows.Visibility.Collapsed;
                this.cNoTriggersAdded.Visibility = System.Windows.Visibility.Visible;
            }
            else {
                this.cNoTriggersAdded.Visibility = System.Windows.Visibility.Collapsed;
                this.cTriggers.Visibility = System.Windows.Visibility.Visible;
                this.UpdateTriggerListPadding();
            }
        }
        #endregion

        #region ICommandConfigurator<ICommand> Members
        public void InitializeConfig(ICommand config) {            
            this._Command = config as TriggerCommand;            
            
            this._IsApply = false;

            // Name and call strings
            this.cName.Text = this._Command.Name;
            this.cCallStrings.Text = String.Join(", ", this._Command.CallStrings.ToArray());
            this.cKeepHistory.IsChecked = this._Command.KeepHistory;
            if (this._Command.KeepHistory == null) {
                this.cHistoryThirdState.Visibility = Visibility.Visible;
            }
            else {
                this.cHistoryThirdState.Visibility = Visibility.Collapsed;
            }

            // Commands
            this.InitializeCommands();

            // Triggers
            this.InitializeTriggers();

            this._IsApply = true;
        }
        public UserControl ConfigPanel { get { return this; } }

        public event EventHandler CommandNameChanged;
        protected virtual void OnCommandNameChanged(EventArgs e) {
            if (this.CommandNameChanged != null) {
                this.CommandNameChanged(this, e);
            }
        }
        #endregion

        #region IScrollable Members
        public ScrollBarVisibility HorizontalScrollBarVisibility { get; set; }
        public ScrollBarVisibility VerticalScrollBarVisibility { get; set; }

        public event ScrollBarVisibilityEventHandler ScrollBarVisibilityChanged;
        // This is never called
        //protected virtual void OnScrollBarVisibilityChanged(object sender, ScrollBarVisibilityEventArgs e) {
        //    if (this.ScrollBarVisibilityChanged != null) {
        //        this.ScrollBarVisibilityChanged(sender, e);
        //    }
        //}
        #endregion

        #region ILoader Members
        public void Load() {
            // Intentionally left blank
        }
        public void Unload() {
            this._Timer.IsEnabled = false;
        }
        #endregion
    }
}
